Passed
Push — master ( 1835a5...4c50ba )
by Rafael S.
02:23
created

main.js ➔ setWriter   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
nc 5
dl 0
loc 1
c 0
b 0
f 0
cc 5
rs 9.3333
nop 1
1
/*
2
 * byte-data: Pack and unpack binary data.
3
 * https://github.com/rochars/byte-data
4
 *
5
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining
8
 * a copy of this software and associated documentation files (the
9
 * "Software"), to deal in the Software without restriction, including
10
 * without limitation the rights to use, copy, modify, merge, publish,
11
 * distribute, sublicense, and/or sell copies of the Software, and to
12
 * permit persons to whom the Software is furnished to do so, subject to
13
 * the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be
16
 * included in all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 *
26
 */
27
28
/**
29
 * @fileoverview The byte-data API.
30
 */
31
32
/** @module byteData */
33
34
import Integer from './lib/integer.js';
35
36
import endianness from 'endianness';
37
38
import {validateType, validateNotUndefined} from './lib/validation.js';
39
40
// Strings
41
/**
42
 * Read a string from a byte buffer.
43
 * @param {!Uint8Array} bytes A byte buffer.
44
 * @param {number=} index The index to read.
45
 * @param {?number=} len The number of bytes to read.
46
 * @return {string}
47
 */
48
export function unpackString(bytes, index=0, len=null) {
49
  let chrs = '';
50
  len = len || bytes.length - index;
51
  for(let j = 0; j < len; j++) {
52
    chrs += String.fromCharCode(bytes[index+j]);
53
  }
54
  return chrs;
55
}
56
57
/**
58
 * Write a string as a byte buffer.
59
 * @param {string} str The string to pack.
60
 * @return {!Array<number>} The next index to write on the buffer.
61
 */
62
export function packString(str) {
63
  let bytes = [];
64
  for (let i = 0; i < str.length; i++) {
65
    bytes[i] = str.charCodeAt(i);
66
  }
67
  return bytes;
68
}
69
70
/**
71
 * Write a string to a byte buffer.
72
 * @param {string} str The string to pack.
73
 * @param {!Uint8Array} bytes A byte buffer.
74
 * @param {number=} index The index to write in the buffer.
75
 * @return {number} The next index to write in the buffer.
76
 */
77
export function packStringTo(str, bytes, index=0) {
78
  for (let i = 0; i < str.length; i++) {
79
    bytes[index] = str.charCodeAt(i);
80
    index++;
81
  }
82
  return index;
83
}
84
85
// Numbers
86
/**
87
 * Pack a number as a byte buffer.
88
 * @param {number} value The number.
89
 * @param {!Object} theType The type definition.
90
 * @return {!Array<number>} The packed value.
91
 * @throws {Error} If the type definition is not valid.
92
 * @throws {Error} If the value is not valid.
93
 */
94
export function pack(value, theType) {
95
  setUp_(theType);
96
  return toBytes_([value], theType);
97
}
98
99
/**
100
 * Pack an array of numbers as a byte buffer.
101
 * @param {!Array<number>} values The values.
102
 * @param {!Object} theType The type definition.
103
 * @return {!Array<number>} The packed values.
104
 * @throws {Error} If the type definition is not valid.
105
 * @throws {Error} If any of the values are not valid.
106
 */
107
export function packArray(values, theType) {
108
  setUp_(theType);
109
  return toBytes_(values, theType);
110
}
111
112
/**
113
 * Pack a number to a byte buffer.
114
 * @param {number} value The value.
115
 * @param {!Object} theType The type definition.
116
 * @param {!Uint8Array} buffer The output buffer.
117
 * @param {number=} index The index to write.
118
 * @return {number} The next index to write.
119
 * @throws {Error} If the type definition is not valid.
120
 * @throws {Error} If the value is not valid.
121
 */
122
export function packTo(value, theType, buffer, index=0) {
123
  setUp_(theType);
124
  return writeBytes_(value,
125
    theType,
126
    buffer,
127
    index,
128
    index + theType.offset,
129
    validateNotUndefined,
130
    theType.be);
131
}
132
133
/**
134
 * Pack a array of numbers to a byte buffer.
135
 * @param {!Array<number>} values The value.
136
 * @param {!Object} theType The type definition.
137
 * @param {!Uint8Array} buffer The output buffer.
138
 * @param {number=} index The buffer index to write.
139
 * @return {number} The next index to write.
140
 * @throws {Error} If the type definition is not valid.
141
 * @throws {Error} If the value is not valid.
142
 */
143
export function packArrayTo(values, theType, buffer, index=0) {
144
  setUp_(theType);
145
  let be = theType.be;
146
  let offset = theType.offset;
147
  for (let i=0; i<values.length; i++) {
148
    index = writeBytes_(
149
      values[i],
150
      theType,
151
      buffer,
152
      index,
153
      index + offset,
154
      validateNotUndefined,
155
      be);
156
  }
157
  return index;
158
}
159
160
/**
161
 * Unpack a number from a byte buffer.
162
 * @param {!Uint8Array} buffer The byte buffer.
163
 * @param {!Object} theType The type definition.
164
 * @return {number}
165
 * @throws {Error} If the type definition is not valid
166
 */
167
export function unpack(buffer, theType) {
168
  setUp_(theType);
169
  let values = fromBytes_(
170
    buffer.slice(0, theType.offset), theType);
171
  return values[0];
172
}
173
174
/**
175
 * Unpack an array of numbers from a byte buffer.
176
 * @param {!Uint8Array} buffer The byte buffer.
177
 * @param {!Object} theType The type definition.
178
 * @return {!Array<number>}
179
 * @throws {Error} If the type definition is not valid.
180
 */
181
export function unpackArray(buffer, theType) {
182
  setUp_(theType);
183
  return fromBytes_(buffer, theType);
184
}
185
186
/**
187
 * Unpack a number from a byte buffer by index.
188
 * @param {!Uint8Array} buffer The byte buffer.
189
 * @param {!Object} theType The type definition.
190
 * @param {number=} index The buffer index to read.
191
 * @return {number}
192
 * @throws {Error} If the type definition is not valid
193
 */
194
export function unpackFrom(buffer, theType, index=0) {
195
  setUp_(theType);
196
  if (theType.be) {
197
    endianness(buffer, theType.offset, index, index + theType.offset);
198
  }
199
  let value = reader_(buffer, index);
200
  if (theType.be) {
201
    endianness(buffer, theType.offset, index, index + theType.offset);
202
  }
203
  return value;
204
}
205
206
/**
207
 * Unpack a array of numbers from a byte buffer by index.
208
 * @param {!Uint8Array} buffer The byte buffer.
209
 * @param {!Object} theType The type definition.
210
 * @param {number=} start The start index. Assumes 0.
211
 * @param {?number=} end The end index. Assumes the buffer length.
212
 * @return {!Array<number>}
213
 * @throws {Error} If the type definition is not valid
214
 */
215
export function unpackArrayFrom(buffer, theType, start=0, end=null) {
216
  setUp_(theType);
217
  if (theType.be) {
218
    endianness(buffer, theType.offset);
219
  }
220
  let len = end || buffer.length;
221
  let values = [];
222
  for (let i=start; i<len; i+=theType.offset) {
223
    values.push(reader_(buffer, i));
224
  }
225
  if (theType.be) {
226
    endianness(buffer, theType.offset);
227
  }
228
  return values;
229
}
230
231
/**
232
 * Unpack a array of numbers to a typed array.
233
 * @param {!Uint8Array} buffer The byte buffer.
234
 * @param {!Object} theType The type definition.
235
 * @param {!TypedArray} output The output array.
236
 * @throws {Error} If the type definition is not valid
237
 */
238
export function unpackArrayTo(buffer, theType, output) {
239
  setUp_(theType);
240
  if (theType.be) {
241
    endianness(buffer, theType.offset);
242
  }
243
  let len = buffer.length;
244
  let outputIndex = 0;
245
  for (let i=0; i<len; i+=theType.offset) {
246
    output.set([reader_(buffer, i)], outputIndex);
247
    outputIndex++;
248
  }
249
  if (theType.be) {
250
    endianness(buffer, theType.offset);
251
  }
252
}
253
254
/**
255
 * @type {!Int8Array}
256
 * @private
257
 */
258
const int8_ = new Int8Array(8);
259
/**
260
 * @type {!Uint32Array}
261
 * @private
262
 */
263
const ui32_ = new Uint32Array(int8_.buffer);
264
/**
265
 * @type {!Float32Array}
266
 * @private
267
 */
268
const f32_ = new Float32Array(int8_.buffer);
269
/**
270
 * @type {!Float64Array}
271
 * @private
272
 */
273
const f64_ = new Float64Array(int8_.buffer);
274
/**
275
 * @type {Function}
276
 * @private
277
 */
278
let reader_;
279
/**
280
 * @type {Function}
281
 * @private
282
 */
283
let writer_;
284
/**
285
 * @type {Object}
286
 * @private
287
 */
288
let gInt_ = {};
289
290
/**
291
 * Turn numbers to bytes.
292
 * @param {number} value The value to be packed.
293
 * @param {!Object} theType The type definition.
294
 * @param {!Uint8Array} buffer The buffer to write the bytes to.
295
 * @param {number} index The index to start writing.
296
 * @param {number} len The end index.
297
 * @param {!Function} validate The function used to validate input.
298
 * @param {boolean} be True if big-endian.
299
 * @return {number} the new index to be written.
300
 * @private
301
 */
302
function writeBytes_(value, theType, buffer, index, len, validate, be) {
303
  while (index < len) {
304
    validate(value, theType);
305
    index = writer_(buffer, value, index);
306
  }
307
  if (be) {
308
    endianness(
309
      buffer, theType.offset, index - theType.offset, index);
310
  }
311
  return index;
312
}
313
314
/**
315
 * Turn a byte buffer into what the bytes represent.
316
 * @param {!Uint8Array} buffer An array of bytes.
317
 * @param {!Object} theType The type definition.
318
 * @return {!Array<number>}
319
 * @private
320
 */
321
function fromBytes_(buffer, theType) {
322
  if (theType.be) {
323
    endianness(buffer, theType.offset);
324
  }
325
  let len = buffer.length;
326
  let values = [];
327
  len = len - (theType.offset - 1);
328
  for (let i=0; i<len; i+=theType.offset) {
329
    values.push(reader_(buffer, i));
330
  }
331
  return values;
332
}
333
334
/**
335
 * Turn numbers to bytes.
336
 * @param {!Array<number>} values The data.
337
 * @param {!Object} theType The type definition.
338
 * @return {!Array<number>} the data as a byte buffer.
339
 * @private
340
 */
341
function toBytes_(values, theType) {
342
  let j = 0;
343
  let bytes = [];
344
  let len = values.length;
345
  for(let i=0; i < len; i++) {
346
    validateNotUndefined(values[i]);
347
    j = writer_(bytes, values[i], j);
348
  }
349
  if (theType.be) {
350
    endianness(bytes, theType.offset);
351
  }
352
  return bytes;
353
}
354
355
/**
356
 * Read int values from bytes.
357
 * @param {!Uint8Array} bytes An array of bytes.
358
 * @param {number} i The index to read.
359
 * @return {number}
360
 * @private
361
 */
362
function readInt_(bytes, i) {
363
  return gInt_.read(bytes, i);
364
}
365
366
/**
367
 * Read 1 16-bit float from bytes.
368
 * Thanks https://stackoverflow.com/a/8796597
369
 * @param {!Uint8Array} bytes An array of bytes.
370
 * @param {number} i The index to read.
371
 * @return {number}
372
 * @private
373
 */
374
function read16F_(bytes, i) {
375
  let int = gInt_.read(bytes, i);
376
  let exponent = (int & 0x7C00) >> 10;
377
  let fraction = int & 0x03FF;
378
  let floatValue;
379
  if (exponent) {
380
    floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
381
  } else {
382
    floatValue = 6.103515625e-5 * (fraction / 0x400);
383
  }
384
  return floatValue * (int >> 15 ? -1 : 1);
385
}
386
387
/**
388
 * Read 1 32-bit float from bytes.
389
 * @param {!Uint8Array} bytes An array of bytes.
390
 * @param {number} i The index to read.
391
 * @return {number}
392
 * @private
393
 */
394
function read32F_(bytes, i) {
395
  ui32_[0] = gInt_.read(bytes, i);
396
  return f32_[0];
397
}
398
399
/**
400
 * Read 1 64-bit float from bytes.
401
 * Thanks https://gist.github.com/kg/2192799
402
 * @param {!Uint8Array} bytes An array of bytes.
403
 * @param {number} i The index to read.
404
 * @return {number}
405
 * @private
406
 */
407
function read64F_(bytes, i) {
408
  ui32_[0] = gInt_.read(bytes, i);
409
  ui32_[1] = gInt_.read(bytes, i + 4);
410
  return f64_[0];
411
}
412
413
/**
414
 * Write a integer value to a byte buffer.
415
 * @param {!Uint8Array} bytes An array of bytes.
416
 * @param {number} number The number to write as bytes.
417
 * @param {number} j The index being written in the byte buffer.
418
 * @return {!number} The next index to write on the byte buffer.
419
 * @private
420
 */
421
function writeInt_(bytes, number, j) {
422
  return gInt_.write(bytes, number, j);
423
}
424
425
/**
426
 * Write one 16-bit float as a binary value.
427
 * @param {!Uint8Array} bytes An array of bytes.
428
 * @param {number} number The number to write as bytes.
429
 * @param {number} j The index being written in the byte buffer.
430
 * @return {number} The next index to write on the byte buffer.
431
 * @private
432
 */
433
function write16F_(bytes, number, j) {
434
  f32_[0] = number;
435
  let x = ui32_[0];
436
  let bits = (x >> 16) & 0x8000;
437
  let m = (x >> 12) & 0x07ff;
438
  let e = (x >> 23) & 0xff;
439
  if (e >= 103) {
440
    bits |= ((e - 112) << 10) | (m >> 1);
441
    bits += m & 1;
442
  }
443
  bytes[j++] = bits & 0xFF;
444
  bytes[j++] = bits >>> 8 & 0xFF;
445
  return j;
446
}
447
448
/**
449
 * Write one 32-bit float as a binary value.
450
 * @param {!Uint8Array} bytes An array of bytes.
451
 * @param {number} number The number to write as bytes.
452
 * @param {number} j The index being written in the byte buffer.
453
 * @return {number} The next index to write on the byte buffer.
454
 * @private
455
 */
456
function write32F_(bytes, number, j) {
457
  f32_[0] = number;
458
  return gInt_.write(bytes, ui32_[0], j);
459
}
460
461
/**
462
 * Write one 64-bit float as a binary value.
463
 * @param {!Uint8Array} bytes An array of bytes.
464
 * @param {number} number The number to write as bytes.
465
 * @param {number} j The index being written in the byte buffer.
466
 * @return {number} The next index to write on the byte buffer.
467
 * @private
468
 */
469
function write64F_(bytes, number, j) {
470
  f64_[0] = number;
471
  j = gInt_.write(bytes, ui32_[0], j);
472
  return gInt_.write(bytes, ui32_[1], j);
473
}
474
475
/**
476
 * Set the function to unpack the data.
477
 * @param {!Object} theType The type definition.
478
 * @private
479
 */
480
function setReader(theType) {
481
  if (theType.float) {
482
    if (theType.bits == 16) {
483
      reader_ = read16F_;
484
    } else if(theType.bits == 32) {
485
      reader_ = read32F_;
486
    } else if(theType.bits == 64) {
487
      reader_ = read64F_;
488
    }
489
  } else {
490
    reader_ = readInt_;
491
  }
492
}
493
494
/**
495
 * Set the function to pack the data.
496
 * @param {!Object} theType The type definition.
497
 * @private
498
 */
499
function setWriter(theType) {
500
  if (theType.float) {
501
    if (theType.bits == 16) {
502
      writer_ = write16F_;
503
    } else if(theType.bits == 32) {
504
      writer_ = write32F_;
505
    } else if(theType.bits == 64) {
506
      writer_ = write64F_;
507
    }
508
  } else {
509
    writer_ = writeInt_;
510
  }   
511
}
512
513
/**
514
 * Validate the type and set up the packing/unpacking functions.
515
 * @param {!Object} theType The type definition.
516
 * @throws {Error} If the type definition is not valid.
517
 * @private
518
 */
519
function setUp_(theType) {
520
  validateType(theType);
521
  theType.offset = theType.bits < 8 ? 1 : Math.ceil(theType.bits / 8);
522
  theType.be = theType.be || false;
523
  setReader(theType);
524
  setWriter(theType);
525
  gInt_ = new Integer(
526
    theType.bits == 64 ? 32 : theType.bits,
527
    theType.float ? false : theType.signed);
528
}
529